package com.ruoyi.framework.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.StringTools;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * @Cacheable配置过期时间
 */
@Configuration
public class RedisCacheConfig {

    @Autowired
    ResourceLoader resourceLoader;

    //常量类路径
    private static final String path = "com.ruoyi.common.constant.CacheNameConstants";

    //一小时
    private static final int hour = 60 * 60;

    //标识
    private static final String $ = "$";

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        return RedisCacheManager.builder(redisConnectionFactory)
                //默认的缓存配置(没有配置键的key均使用此配置)
                .cacheDefaults(getDefaultCacheConfiguration())
                .withInitialCacheConfigurations(getCacheConfigurations())
                //在spring事务正常提交时才缓存数据
                .transactionAware()
                .build();
    }

    private Map<String, RedisCacheConfiguration> getCacheConfigurations() {
        Map<String, RedisCacheConfiguration> configurationMap = new HashMap<>();
        getAllStaticField(configurationMap);
        return configurationMap;
    }

    private void getAllStaticField(Map<String, RedisCacheConfiguration> configurationMap) {
        try {
            Class clazz = Class.forName(path);
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field field : declaredFields) {
                //可以訪問私有屬性
                field.setAccessible(true);
                //只获取静态属性
                if (Modifier.isStatic(field.getModifiers())) {
                    //字段名
                    String name = field.getName();
                    int index = name.indexOf($);

                    //不带$ 则不指定失效时间，为永久时间
                    if (index == -1) {
                        continue;
                    }

                    //字段值
                    String cacheValue = StringTools.objString(field.get(clazz));

                    //获取失效时间
                    String nums =  name.substring(index+1);
                    if (StringTools.isBlank(nums)) {
                        throw new CustomException(" CacheNameConstants 类中 "+name+" 属性不符合规范的命名!");
                    }
                    Integer num = StringTools.objInt(nums);
                    int time;

                    //数值等于0时，缓存默认失效时间为15秒
                    if (num == 0 ) {
                        time = 15;
                    }else {
                        time = hour * num;
                    }
                    //设置缓存失效时间
                    configurationMap.put(cacheValue, this.getDefaultCacheConfiguration(time));
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取redis的缓存配置(针对于键)
     *
     * @param seconds 键过期时间
     * @return
     */
    private RedisCacheConfiguration getDefaultCacheConfiguration(long seconds) {
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
                RedisSerializationContext
                        .SerializationPair
                        .fromSerializer(jackson2JsonRedisSerializer)
        ).entryTtl(Duration.ofSeconds(seconds));
        return redisCacheConfiguration;
    }

    /**
     * 获取Redis缓存配置,此处获取的为默认配置
     * 如对键值序列化方式,是否缓存null值,是否使用前缀等有特殊要求
     * 可另行调用 RedisCacheConfiguration 的构造方法
     *
     * @return
     */
    private RedisCacheConfiguration getDefaultCacheConfiguration() {
        // 但观察源码可得核心不过是在值序列化器(valueSerializationPair)的构造中注入 ClassLoader 即可
        return RedisCacheConfiguration.defaultCacheConfig(resourceLoader.getClassLoader());
    }

}
